package physics;

import java.util.List;

import model.BoundingBox;

import org.lwjgl.util.vector.Vector3f;

import entity.Entity;
import entity.MovingEntity;
import entity.Ground;
import entity.characters.Player;
import entity.characters.Character;
import entity.item.Item;
import entity.weapon.Shot;

/**
 * 
 * @author Jevgeni Krutov
 *
 */
public class Collision {
	private BoundingBox bb1;
	private BoundingBox bb2;
	
	private Vector3f pos1;
	private Vector3f pos2;
	
	public boolean detect(Entity entity1, Entity entity2) {
		pos1 = entity1.getCenterPoint();
		pos2 = entity2.getCenterPoint();	
		float diffX = Math.abs(pos1.x - pos2.x);
		float diffZ = Math.abs(pos1.z - pos2.z );
		float diffY = Math.abs(pos1.y - pos2.y);
		
		bb1 = entity1.getModel().getBoundingBox();
		bb2 = entity2.getModel().getBoundingBox();
		
		if(diffX < (bb1.getHalfX() + bb2.getHalfX()) ) {
			if(diffZ < (bb1.getHalfZ() + bb2.getHalfZ()) ) {
				if(diffY < (bb1.getHalfY() + bb2.getHalfY()) ) {
					return true;
				}
			}
		}
		return false;
	}
	
	private float getRequiredReboundDistance(Entity entity1, Entity entity2, Vector3f vectorBetweenCenters) {
		Vector3f okDistanceOnEachAxisModule = new Vector3f(
				bb1.getHalfX() + bb2.getHalfX(),
				bb1.getHalfY() + bb2.getHalfY(),
				bb1.getHalfZ() + bb2.getHalfZ());
			
		Vector3f distanceModule = new Vector3f(
				Math.abs(vectorBetweenCenters.x),
				Math.abs(vectorBetweenCenters.y),
				Math.abs(vectorBetweenCenters.z));//positive vector pointing model1 -> model2
				
		Vector3f intersection = new Vector3f(Vector3f.sub(okDistanceOnEachAxisModule, distanceModule, null));
		
		setZeroToTwoBiggestComponents(intersection);

		return 1.05f/*additional space*/ * Math.abs(Vector3f.dot(intersection, distanceModule));
	}
	
	public void handleGroundCollision(Entity entity, Ground ground) {
		float feetLevel = entity.getPosition().y - entity.getModel().getBoundingBox().getHalfY();
		float groundLevel = ground.getPosition().y + ground.getModel().getBoundingBox().getHalfY();
		float deltaY = groundLevel - feetLevel;
		
		entity.addPosition(0, deltaY, 0);
		if(entity.isCharacter()) {
			((Character) entity).setGrounded(true);
		}
	}
	
	public void collide(Entity entity1, Entity entity2) {
		if(entity1 == entity2) {//an entity cannot collide with itself
			return;
		}
		
		if(entity1.isItem() || entity2.isItem()) {
			giveItemToPlayerIfNeeded(entity1, entity2);
			return;
		}
		
		if(collideWithGroundIfNeeded(entity1, entity2)) {
			rocketCollision(entity1, entity2);
			return;
		}
		
		Vector3f vectorBetweenCenters = new Vector3f(); //vector pointing model1 -> model2
		Vector3f.sub(entity2.getCenterPoint(), entity1.getCenterPoint(), vectorBetweenCenters);
		float reboundDistance = getRequiredReboundDistance(entity1, entity2, vectorBetweenCenters);//minimum distance for objects to be apart on vectorBetweenCenters axis
		if(vectorBetweenCenters.x == 0f || vectorBetweenCenters.y  == 0f || vectorBetweenCenters.z  == 0f) {
			vectorBetweenCenters.x = 1f;
		}
		vectorBetweenCenters.normalise();
		
		//then model1 goes direction -vectorBetweenCenters, model2 goes direction +vectorBetweenCenters
		Vector3f positionRebound1 = new Vector3f();
		vectorBetweenCenters.negate(positionRebound1);
		Vector3f positionRebound2 = vectorBetweenCenters;
		
		if(entity1.isMovingEntity() && entity2.isMovingEntity()) {
			positionRebound1.scale(reboundDistance * (entity2.getMass() / (entity1.getMass() + entity2.getMass())));
			positionRebound2.scale(reboundDistance * (entity1.getMass() / (entity1.getMass() + entity2.getMass())));
		} else {
			if(entity1.isMovingEntity())
				positionRebound1.scale(reboundDistance);
			if(entity2.isMovingEntity())
				positionRebound2.scale(reboundDistance);
		}
		
		entity1.addPosition(positionRebound1);
		entity2.addPosition(positionRebound2);
		
		
		//some rebound force for further realism:
		
		entity1.applyForce((Vector3f) positionRebound1.scale(entity2.getMass() * 10f));
		entity2.applyForce((Vector3f) positionRebound2.scale(entity1.getMass() * 10f));
		
		rocketCollision(entity1, entity2);
		
		
		//law of conservation of momentum (simplified)
		
		Vector3f momentum1 = new Vector3f(entity1.getSpeed());
		momentum1.scale(10 * entity1.getMass());
		
		Vector3f momentum2 = new Vector3f(entity2.getSpeed());
		momentum2.scale(10 * entity2.getMass());
		
		entity1.applyForce(momentum2);
		entity2.applyForce(momentum1);
	}

	private void giveItemToPlayerIfNeeded(Entity entity1, Entity entity2) {
		//if the method was called, then at least one of the entities is item	
		if(!entity1.isPlayer() && !entity2.isPlayer()) {
			return;
		}
		
		Player player;
		Item item;
		
		if(entity1.isPlayer()) {
			player = (Player) entity1;
			item = (Item) entity2;
		} else {
			player = (Player) entity2;
			item = (Item) entity1;
		}
		
		player.pickItem(item);		
	}

	private boolean collideWithGroundIfNeeded(Entity entity1, Entity entity2) {
		if(entity1.isGround()) {
			handleGroundCollision(entity2, (Ground) entity1);
			return true;
		}
		if(entity2.isGround()) {
			handleGroundCollision(entity1, (Ground) entity2);
			return true;
		}
		return false;
	}

	private void rocketCollision(Entity entity1, Entity entity2) {
		if(entity1.isShot()) {
			((Shot) entity1).destroy();
			if(entity2.isCharacter()) {
				((Character) entity2).applyDamage(((Shot) entity1).getDamage());
			}
		}
		if(entity2.isShot()) {
			((Shot) entity2).destroy();
			if(entity1.isCharacter()) {
				((Character) entity1).applyDamage(((Shot) entity2).getDamage());
			}
		}
	}

	private void setZeroToTwoBiggestComponents(Vector3f intersection) {
		if(intersection.x < intersection.y) {
			intersection.y = 0;
			if(intersection.x < intersection.z) {
				intersection.z = 0;
			} else {
				intersection.x = 0;
			}
		} else {
			intersection.x = 0;
			if(intersection.y < intersection.z) {
				intersection.z = 0;
			} else {
				intersection.y = 0;
			}
		}
	}
}
